package eclant.eclipse.jdt;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

public class ClasspathWalker
{

	public static void walkClasspath(IJavaProject project, boolean exportedOnly,
			ClasspathVisitor result) throws JavaModelException
	{
		ClasspathWalker collector = new ClasspathWalker(result, exportedOnly);
		result.enter(project);
		collector.collectFiles(project, false);
		result.exit();
	}

	private final IWorkspaceRoot root;
	private final ClasspathVisitor result;
	private final boolean exportedOnly;
	private final Set<String> visited = new HashSet<String>();

	private ClasspathWalker(ClasspathVisitor result, boolean exportedOnly)
	{
		this.result = result;
		this.exportedOnly = exportedOnly;
		this.root = ResourcesPlugin.getWorkspace().getRoot();
	}

	private void collectFiles(IJavaProject javaProject, boolean exportedOnly)
			throws JavaModelException
	{
		IClasspathEntry[] classpath = javaProject.getRawClasspath();
		for (IClasspathEntry entry : classpath)
		{
			if (!exportedOnly || entry.isExported())
				collectFiles(entry, javaProject);
		}
	}

	private boolean firstEncounter(IClasspathEntry entry)
	{
		String pathStr = entry.getPath().toPortableString();
		return visited.add(pathStr);
	}

	private void collectFiles(IClasspathEntry entry, IJavaProject javaProject)
			throws JavaModelException
	{
		IPath path;
		switch (entry.getEntryKind())
		{
			case IClasspathEntry.CPE_CONTAINER:
				IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(),
						javaProject);
				IClasspathEntry[] classpath = container.getClasspathEntries();
				if (result.enter(container))
				{
					for (IClasspathEntry cpe : classpath)
						collectFiles(cpe, javaProject);
					result.exit();
				}
				break;
			case IClasspathEntry.CPE_LIBRARY:
				IResource member = root.findMember(entry.getPath());
				if (member == null)
					result.externalLibrary(entry.getPath(), !firstEncounter(entry));
				else if (member.getType() == IResource.FILE)
					result.library((IFile) member, !firstEncounter(entry));
				else
					result.library((IFolder) member, !firstEncounter(entry));
				break;
			case IClasspathEntry.CPE_PROJECT:
				IProject proj = root.getProject(entry.getPath().toPortableString());
				IJavaProject jproj = JavaCore.create(proj);
				if (firstEncounter(entry))
				{
					if (result.enter(jproj))
					{
						collectFiles(jproj, this.exportedOnly);
						result.exit();
					}
				}
				else
				{
					result.previouslyEncountered(jproj);
				}
				break;
			case IClasspathEntry.CPE_SOURCE:
				IFolder srcFolder = root.getFolder(entry.getPath());
				path = entry.getOutputLocation();
				IFolder outFolder = (path == null ? null : root.getFolder(path));
				result.source(javaProject, srcFolder, outFolder, !firstEncounter(entry));
				break;
			case IClasspathEntry.CPE_VARIABLE:
				IClasspathEntry resolved = JavaCore.getResolvedClasspathEntry(entry);
				if (resolved != null)
				{
					result.resolved(entry, resolved);
					collectFiles(resolved, javaProject);
				}
				else
				{
					result.resolveFailed(entry);
				}
				break;
		}
	}
}
